<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        // call方法: 调用函数(构造函数)
        // 语法: 函数名.call()
        function fn() {
            console.log(123);
        }
        fn();
        // 通过call方法调用fn函数
        fn.call();
        1. 通过call调用函数(普通函数 + 构造函数)
        2. 调用函数的时候, 修改函数中的this指向
        3. 调用带有参数的函数, 函数.call(具体的对象, 实参1, 实参2);




        // 属性继承
        // 继承: 利用到call方法 
        // a) call调用函数
        // 函数名.call()
        // b) call修改函数中的this指向
        // 函数名.call(对象, 其他参数)


        // 继承: 在js中的继承,同样可以减少代码量
        // a) 属性继承
        // b) 方法继承


        // 属性继承演示:
        function Father(uname, uage, ugender) {
            //this: 指向Father创建的实例对象
            this.uname = uname;
            this.uage = uage;
            this.ugender = ugender;
        }
        let zs = new Father('大张三', 35, '男');
        // 在Son构造函数中不写this.. ,同时还要实现让Son构造函数拥有uname,uage,ugender
        // 解决途径: 在Son构造函数中调用Father构造函数
        function Son(uname, uage, ugender, score) {
            // this: 永远都要指向Son创建的实例对象
            // 调用Father构造函数的时候,需要将Faher中的this修改为当前Son中的this指向

            // 因为这样设置后,相当于是设置了静态成员,无法通过实例对象访问
            // Father.call(Son, uname, uage, ugender);

            // 一定要写this关键字, this指向的是实例对象
            // Father.call(this, uname, uage, ugender);
            let res = Father.bind(this, uname, uage, ugender);
            res();
            this.score = score;
        }
        let xzs = new Son('小张三', 10, '女', 100);
        console.log(xzs);
        // 方法继承(原型继承)
        Son.prototype = new Fater();





        // 类:es6中提出一个概念
        // 类的本质就是一个构造函数
        // 类的作用: 创建对象


        // 类使用方式:
        // a) 定义类 : class 自定义类名 {}
        // b) 通过类创建对象:  new 类名();
        // c) 通过类创建的对象叫 实例对象

        // 代码演示:
        // 定义学生类
        class Student {
            constructor() {
                属性内容
            }
        };
        // 通过类创建对象
        let zs = new Student();
        console.log(zs);



        // 通过类设置属性和方法
        // 设置属性
        // 语法: 通过 constructor函数设置属性

        // 设置方法:
        // 必须和constructor是并列关系,方法单独设置,相当于一个原型函数
        class Student {
            // 属性
            constructor(uname, uage, ugender) {
                this.uname = uname;
                this.uage = uage;
                this.ugender = ugender;
            }
            // 方法
            eat() {
                console.log('吃饭...');
            }
            // 方法
            sing() {
                console.log('唱歌');
            }
        }
        // 创建zs实例对象
        let zs = new Student('张三', 23, '男');
        // zs.eat();
        // zs.sing();
        console.log(zs);


        // 总结:
        // 1. 在类中通过 constructor设置属性
        // 2. 在类中设置方法,必须写到 constructor外部
        // 3. 在类中的方法,直接写方法即可,不能加function关键字





        //构造函数实现属性继承:
        //a) 在子构造函数中通过: 
        父构造函数.call(this, 参数);
        //b) 子构造函数要继承父构造函数中的方法:    
        子构造函数.prototype = new 父构造函数
        // 通过类实现继承:
        子类 extends 父类 { }

        // 代码演示:
        class F {
            constructor(uname, uage, ugender) {
                this.uname = uname;
                this.uage = uage;
                this.ugender = ugender;
            }
            eat() {
                console.log('123');
            }
        }
        // 继承
        class S extends F { }
        let zs = new S('张三', 23, '男');
        zs.eat();
        console.log(zs);



        //类总结
        // 1. 类中的this也指向创建的实例对象
        // 2. 类中的属性和方法之间相互访问的时候,必须加this关键字
        // 3. 类实现继承,通过 类名 extends 类名 {}
        // 4. 类继承的过程中,如果子类没有设置自己的构造函数,则直接使用
        // 5. 类继承的时候,如果子类中有自己的构造函数, 必须在构造函数中this前面加 super()
        // 6. super 调用父类中的构造函数
    </script>
</body>

</html>